fix(proxy): forward content-frame API calls + errors to chrome shell - #2
Closed
andylbrummer wants to merge 2 commits into
Closed
andylbrummer wants to merge 2 commits into
andylbrummer wants to merge 2 commits into
Conversation
The always-wrap shell model renders a top-level navigation as a chrome shell wrapping the real page in a content <iframe>. The indicator UI runs in the shell, but fetch/XHR capture (api-tracker.js) and error capture (core.js, content-only) happen in the content frame. The shell's own buffers stayed empty, so the Network and Errors tabs (and their badges) never updated with the page's real traffic. Content frames now forward each captured call/error up to the shell via same-origin window.parent.__devtool_*_ingest (the existing frames.js cross-frame idiom). Shell-side terminal ingest sinks push into the shell's own buffers, so getCalls / getDeduplicatedErrors / getStats / audits all work unchanged. Chrome stops recording its own WS/screenshot traffic as Network noise. Standalone (no-shell) pages keep local capture — forward is gated on role==='content'. Verified end-to-end in headless Chrome (AGNT_LIVE_BRIDGE=1): a content fetch + uncaught throw + console.error all surface in the shell frame's buffers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMry6m6QXTdc8Xbecqmzid
Document that shell-side ingestForwardedError intentionally skips the consolidated error stream — harmless today (no shell consumer reads getConsolidated*), but a future consumer must wire it in. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMry6m6QXTdc8Xbecqmzid
andylbrummer
pushed a commit
that referenced
this pull request
Jul 10, 2026
…t lines (G3)
Adds a structured-parser layer over the G2 catch-all (internal/overlay):
recognized multi-line/noisy error blocks fold into a single
{kind, message, file:line} alert instead of surfacing as several `unparsed`
lines. Closes DoD #2 of the get_errors epic.
- structured_parsers.go: StructuredError + Compact(); parsePrisma (folds the
`prisma:error` header + `Invalid \`prisma.x.y()\` invocation` banner + bare
file:line call-site + cause into one line, extracting op + file:line + a
remediation hint); parseDBAuth (raw pg/mysql `password authentication failed`,
`database "x" does not exist`, `too many connections`). Registry +
runStructuredParsers + isStructuralPrefix. Anchors informed by R1
(docs/error-formats/prisma-orm-sql.md).
- alerts.go: ProcessLine runs structured parsers on unclassified lines BEFORE
the catch-all; structural-prefix lines (block header/banner) are folded at
the cause line rather than surfaced standalone; adds recentSnapshot().
- ECONNREFUSED intentionally left to the existing `connection-refused` pattern;
stack frames are already non-surfaced (don't trip the catch-all).
Prisma DB-auth block now renders as one compact line carrying the cause +
prisma.user.findUnique() @ /app/src/db.ts:42 — measurably shorter than the raw
block (asserted). G1 catch-all contract preserved (block still surfaces, now
structured). testing-review passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
andylbrummer
pushed a commit
that referenced
this pull request
Jul 10, 2026
…lking)
Replaced the hand-rolled map[string]interface{} walking in all 8
StatusFetcher.fetch* methods with typed DTOs (status_dto.go) decoded via
json.Unmarshal. Each per-field `m["x"].(string)` / `.(float64)` access is gone;
fetchers now decode into a json-tagged DTO and convert with a small toInfo().
This eliminates the brittle pattern that caused the earlier empty-event-log bug
(fetchStartupLog silently missing a field/key): adding or renaming a status
field is now a typed DTO change in one place instead of matching string keys by
hand across map walks. Behavior is unchanged — same daemon calls, same display
structs out; the overlay tests pass.
Note: this is the safe, type-safety half of the "#2" overview work. The
larger LOC collapse (8 fetches -> 1 batched OVERVIEW verb) was intentionally
NOT done: it would force the daemon to re-derive per-proxy log/page data
in-process (divergence risk) for no real latency gain now that the heavy
per-tick scans are cached. Net LOC here is ~flat; the win is type safety and
single-site field edits, not line count.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
andylbrummer
force-pushed
the
fix/proxy-ui-content-shell-bridge
branch
from
July 10, 2026 22:57
7aacd8d to
8502aeb
Compare
andylbrummer
pushed a commit
that referenced
this pull request
Jul 18, 2026
Five operator-reported rough edges surfaced from a live session: - monitor firehose (#3): a single page load emitted per-node DOM mutations by the hundred, blowing the downstream consumer's rate limit. Add a mutationCoalescer that folds a same-target burst into one rolling "[mutation] N changes (M nodes)" summary (500ms / target-change / 50-cap buckets, tail-flush on disconnect). Compact stream only; JSON stays 1:1. - raw gzip in compact log (#4): formatCompact/formatJSON dumped ResponseBody raw, but the traffic recorder captures on-the-wire bytes that stay compressed for any response not decompressed for HTML injection. New httpBodyPreview renders "<gzip-encoded, N bytes>" / "<binary, N bytes>" instead of garbage. - misleading exec-timeout diagnostic (#5): the hub timeout message echoed the raw frame arg (usually ""), reading as "no frame" even when the exec was routed to the active content frame. New ProxyServer.DescribeExecTarget makes the message report the resolved destination plus a heavy-screenshot hint. - access-URL typo (#6): listenAddr is a full host:port, so "http://localhost" + listenAddr produced "http://localhost127.0.0.1:47341". Extract testable proxyAccessURL; 0.0.0.0 now renders "http://<your-ip>:PORT". - daemon start help (#6): the command blocks in the foreground but the help text implied auto-start/detach. Spell out that it holds the terminal until Ctrl+C and does not detach. Also determinize TestPageTracker_SessionTimeout, which flaked in the pre-commit race suite (unrelated to the above): its Active flag is recomputed against wall-clock now on each read, so a single fixed 5ms sleep can race the scheduler on an oversubscribed host. Poll for the post-timeout invariant with generous headroom instead. Ref .claude/rules/testing-timing-assertion-flakes.md. (Reported item #2 — watch emitting --target — was already fixed; the tool emits --types. Verified against the built binary, no change needed.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
andylbrummer
pushed a commit
that referenced
this pull request
Jul 19, 2026
Replaces the three LOCAL vendored process-lifecycle patches (agnt commits 77a98d2/613c74e9, b964cb2/e74fad05, 3804dda) with released upstream code now that they merged and shipped as go-cli-server v0.5.7: - PR #1 recycled-PID cleanup guard (carry scanner-captured identities through post-Wait cleanup instead of re-sampling a possibly-reused PID/PGID) - PR #2 serialize spawn vs shutdown for StateStarting procs (startMu-ordered final-check/spawn/publish; ctx-bounded Shutdown wait) - PR #3 negative-PID signal only a verified stable group leader No go.mod replace directive. Vendored tree matches the released v0.5.7 tag exactly. agnt's own internal/platform/* changes stay local (not upstreamed). Verified: go vet, GOOS=windows cross-build, and full 'go test -p 1 ./...' serial suite all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Rk1F3dUJLaL6f9tqu5fRA
andylbrummer
pushed a commit
that referenced
this pull request
Aug 20, 2026
…gnals The recorder's fixed +1200ms settle after __devtool appears is flaky under CPU load (too short) and wasteful when idle (too long). Pin the replacement to the ACTUAL signals a live bundle exposes — indicator/toast mounted AND the websocket transport OPEN — so the gate keys on the connection being live, not on a timer that fires before it (lessons-ssh-transport.md #2). Predicates are not yet exported: RED. worktrack-task: 01M08GG2XSD0PT8RRA8GC9K2RE Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The proxy UI tabs (Network, Errors) and their badges stopped updating with the page's real API calls and front-end errors.
Root cause: the always-wrap shell model (
injector.goBuildShellDocument, default for top-level navigations) renders a page as a chrome shell wrapping the real page in a content<iframe>. The indicator UI runs in the shell frame, but:api-tracker.jswrapsfetch/XHR per-frame → app calls land in the content frame'scallBuffer.core.jserror capture runs only in the content frame (__isContentgate at init) → JS/console errors land in the content frame's buffers.The shell's own buffers stayed empty, so
getCalls()/getDeduplicatedErrors()/getStats()— which feed the tabs + badges via the 1s poll — saw nothing.Fix
Content frames forward each captured call/error up to the shell via the existing same-origin cross-frame idiom (
frames.jswindow.parent.__devtool_*):api-tracker.js—addCallforwards toshell.__devtool_api_ingestwhenrole==='content'; new terminal ingest sink; chrome stops recording its own WS/screenshot fetches as Network noise.core.js—forwardErrorToShell+ingestForwardedError; forward at the js/console/warn buffer pushes;window.__devtool_errors_ingestexport.Shell ingest pushes into the shell's own buffers, so
getCalls/getDeduplicatedErrors/getStats/ audits all work unchanged. No loop (shell is terminal). Backward compatible — standalone (no-shell) pages keep local capture; forward is gated onrole==='content'.Verification
Opt-in headless-Chrome regression test (
bridge_live_test.go,AGNT_LIVE_BRIDGE=1). A content-frame fetch + uncaught throw +console.errorall surface in the shell frame:{"role":"chrome","urls":["…/api/data"],"stats":{"jsErrorCount":1,"consoleErrorCount":1,"totalCount":2}}Normal suite green (
go test ./internal/proxy/...); live test skips unless gated.🤖 Generated with Claude Code